package com.itholmes;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

//实现IOC容器和DI的类
public class ClassPathXmlApplicationContext {
	
	Map<String,Object> ioc = new HashMap<String, Object>();
	
	public ClassPathXmlApplicationContext(String path) {
		
		SAXReader reader = new SAXReader();
		try {
			Document document = reader.read(path);
			//拿到根元素beans标签中的内容，并且进行遍历
			Element rootElement = document.getRootElement();
			Iterator<Element> beans = rootElement.elementIterator();
			
			//这个while循环是为了创建对应的bean对象，然后按照id和bean对象，键值对的形式存到ioc(map)中。
			while(beans.hasNext()) {
				Element bean = beans.next();
				//获取属性id和class
				Attribute id_attribute = bean.attribute("id");
				Attribute class_attribute = bean.attribute("class");
				//获取分别属性id和class对应的值。
				String id_value = id_attribute.getValue();
				String class_value = class_attribute.getValue();
				
				//接下来就是将id属性对应的值 和 创建一个类路径为class_value的对象 ， 进而存在map中。
				//创建对象，就是用反射来创建就可以了。
				Class<?> c = Class.forName(class_value);
				Object object = c.newInstance();
				ioc.put(id_value, object);
				
				System.out.println();
			}
			
			//注意这里我们之所以新创建一个beans2，而不用上面的beans就是因为迭代器的指针已经指向最后一个了。
			Iterator<Element> beans2 = rootElement.elementIterator();
			while(beans2.hasNext()) {
				
				Element bean = beans2.next();
				Attribute id_attribute = bean.attribute("id");
				//拿到bean的id的value值。
				String id_value = id_attribute.getValue();
				
				Iterator<Element> childs = bean.elementIterator();
				while(childs.hasNext()) {
					Element child = childs.next();
					String child_name = child.getName();
					if("property".equals(child_name)) {
						
						//拿到property每一个标签的属性
						Attribute name_attribute = child.attribute("name");
						Attribute value_attribute = child.attribute("value");
						
						//获取每一个属性的属性值。
						String name_value = name_attribute.getValue();
						
						//我们想要给对象注入值，就必须调用set方法也就是set注入，怎么获取set方法？还是通过反射。
						//首先，我们想将方法名拼接出来 set+首字母大写的name_value
						String methodName = getMethodName(name_value);
						
						//有了方法名，我们就可以在ioc中，通过id来拿到对象，同构反射来调用方法了。
						Object obj = ioc.get(id_value);
						
						//通过对象反射和方法名，获取到method。但是需要注意因为所有的set方法都是有参数的，这里我们也要传入参数！
						//并且我们还要判断参数的类型，因为从xml文件拿到的类型都是String类型
						Class<? extends Object> c = obj.getClass();
						
						//判断我们set方法注入的类型，就需要反射的field获取属性。
						Field field = c.getDeclaredField(name_value);
						Method method = c.getDeclaredMethod(methodName, field.getType());
						
						//不要忘记ref，这里是如果value属性对应的值为null，那说明用的是ref的属性。
						if(value_attribute!=null) {
							
							String value_value = value_attribute.getValue();
							
							Class<?> type = field.getType();
							Integer i = 0;
							if(type.toString().equals("int")) {
								if(value_value==null||value_value.equals("")) {
									value_value = "0";
								}
								i = Integer.parseInt(value_value);
								//对于int类型我们必须转换类型。当然如果还有其他类型必须在做判断
								Object invoke = method.invoke(obj, i);
							}else {
								//从而我们使用反射方法的三要素就可以调用set注入了。
								Object invoke = method.invoke(obj, value_value);
							}
							
						}else{
							Attribute ref_attribute = child.attribute("ref");
							String ref_value = ref_attribute.getValue();
							Object object = ioc.get(ref_value);
							
							Object invoke = method.invoke(obj, object);
							
						}
						System.out.println();
					}
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	//模拟容器中的getBean方法
	public Object getBean(String id) {
		return ioc.get(id);
	}
	
	//通过拼接获取方法的名字也就是set+首字母大写的name_value
	public String getMethodName(String name) {
		String shouzimu = name.substring(0,1);
		String upperCase = shouzimu.toUpperCase();
		String result = "set"+upperCase+name.substring(1);
		return result;
	}
	
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("src/resources/Spring.xml");
		Object person1 = ioc.getBean("person1");
		System.out.println(person1.toString());
	}
}
